﻿using FundHelper.Model;
using FundHelper.Utility;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using static OfficeOpenXml.ExcelErrorValue;

namespace FundHelper.Model
{
    public static partial class Indicator
    {
        /// <summary>
        /// 计算MACD
        /// </summary>
        /// <param name="list"></param>
        /// <param name="fastPeriods">短周期</param>
        /// <param name="slowPeriods">长周期</param>
        /// <param name="signalPeriods">计算dea周期</param>
        /// <returns>返回KDJ，其中现在最后</returns>
        /// <exception cref="ArgumentException"></exception>
        public static IEnumerable<MacdResult> GetMacdResult(this IEnumerable<(DateTime date, double price)> list, int fastPeriods = 12,
                int slowPeriods = 26,
                int signalPeriods = 9)
        {
            var tuples = list.ToList();
            if (tuples.Count < slowPeriods*3)
                throw new ArgumentException("无足够的数据计算Macd,>tuples.count>3*slowPeriods>fastPeriods>signalPeriods");
            var macdArry = new MacdResult[tuples.Count - slowPeriods];
            //Ema会自动把现在的放在最后
            var fastEma=GetEmaResult(tuples,fastPeriods);
            var slowEma = GetEmaResult(tuples, slowPeriods);
            var diffs = fastEma.Join(slowEma, a => a.dateTime, b => b.dateTime,(a,b)=>new { dateTime = a.dateTime, fast = a.Value, slow = b.Value,value=a.Value-b.Value });

            var deas=diffs.Select(a => (a.dateTime, a.value)).ToList().GetEmaResult(signalPeriods);

            var macds=diffs.Join(deas, a => a.dateTime, b=>b.dateTime, (a, b) => new MacdResult(a.dateTime, a.value, b.Value));
            return macds;
        }
        public static IEnumerable<MacdPlusResult> GetMacdPlusResult(this IEnumerable<(DateTime date, double price)> list, int fastPeriods = 12,
        int slowPeriods = 26,
        int signalPeriods = 9)
        {
            var tuples = list.ToList();
            if (tuples.Count < slowPeriods * 3)
                throw new ArgumentException("无足够的数据计算Macd,>tuples.count>3*slowPeriods>fastPeriods>signalPeriods");
            var macdArry = new MacdResult[tuples.Count - slowPeriods];
            //Ema会自动把现在的放在最后
            var fastEma = GetEmaResult(tuples, fastPeriods);
            var slowEma = GetEmaResult(tuples, slowPeriods);
            var diffs = fastEma.Join(slowEma, a => a.dateTime, b => b.dateTime, (a, b) => new { dateTime = a.dateTime, fast = a.Value, slow = b.Value, value = a.Value - b.Value });

            var deas = diffs.Select(a => (a.dateTime, a.value)).ToList().GetEmaResult(signalPeriods);

            var macds = diffs.Join(deas, a => a.dateTime, b => b.dateTime, (a, b) => new MacdResult(a.dateTime, a.value, b.Value)).OrderByDescending(a=>a.DateTime).ToArray();
            var macdPlusArry = new MacdPlusResult[macds.Count()];
            for (int i=0;i< macds.Count();i++)
            {
                double area = 0;
                int j = i;
                for (;j< macds.Count(); j++)
                {
                    if (macds[i].Value * macds[j].Value < 0)
                        break;
                    area += macds[j].Value;
                }
                macdPlusArry[i] = new MacdPlusResult(macds[i].DateTime, macds[i].Dif, macds[i].Dea)
                {
                    Area = area,
                    LastDiffDay = j-i
                };
            }
            return macdPlusArry;
        }
    }


    public class MacdResult(DateTime dateTime, double dif,double dea):IndicatorResult(dateTime, 2f * (dif - dea))
    {
        [IndicatorName("macd")]
        public override double Value => base.Value;
        [IndicatorName("dif")]
        public double Dif { get => dif; set => dif = value; }
        [IndicatorName("dea")]
        public double Dea { get => dea; set => dea = value; }
        public void Deconstruct(out double dif,out double dea,out double macd)
        {
            dif = this.Dif;
            dea = this.Dea;
            macd = this.Value;
        }
    }

    internal class MacdResultCollection : LagCollection<MacdResult>
    {
        public MacdResultCollection(IEnumerable<MacdResult> collection) : base(collection)
        {
        }
        internal static MacdResultCollection Create(IEnumerable<ICanToTuple> his)
        {
            var tupleList = his.ToTuple();
            var macdResultArry = tupleList.GetMacdResult(12, 26, 9).Reverse();
            return new MacdResultCollection(macdResultArry);
        }
        public void Deconstruct(out double[] difs, out double[] deas, out double[] macds)
        {
            int length = this.Collection.Length;  // 或者 this.Collection.Count() 如果是 IEnumerable
            difs = new double[length];
            deas = new double[length];
            macds = new double[length];

            // 单次遍历填充数组
            for (int i = 0; i < length; i++)
            {
                var item = this.Collection[i]; // 假设 Collection 是一个数组或 List
                difs[i] = item.Dif;
                deas[i] = item.Dea;
                macds[i] = item.Value;
            }
        }
    }

    public class MacdPlusResult(DateTime dateTime, double dif, double dea) : MacdResult(dateTime, dif, dea)
    {

        public double Area;
        public int LastDiffDay;
    }

    public struct MacdPlusChangeCollection
    {
        //DateArr、Idxes负责实际的搜索
        private readonly DateTime[] DateArry;
        private readonly int[] Idxes;

        //MacdPlusArr,MacdPlusChangePoints负责根据搜索结果提供数据
        private readonly ImmutableArray<MacdPlusResult> MacdPlusArr;
        private readonly ImmutableArray<MacdPlusResult> MacdPlusChangePoints;

        public static readonly MacdPlusChangeCollection Default = new MacdPlusChangeCollection();

        public bool IsDefault => this.Equals(Default);
        public MacdPlusChangeCollection(IEnumerable<MacdPlusResult> macdPlusResults)
        {
            if (macdPlusResults == null)
                throw new ArgumentNullException(nameof(macdPlusResults));

            // 这里要做个排序，因为影响后面i+1的取值，所以是非常必要的。
            var arr = macdPlusResults.OrderByDescending(a => a.DateTime)
                .ToArray();
            var count = arr.Length; // 使用转换后的数组长度


            var used = Enumerable.Range(0, count - 1)
                .Where(i => arr[i].LastDiffDay == 1)
                .Select(i => new { Value = arr[i + 1], Idx = i });
                
            Idxes = used.Select(a => a.Idx).ToArray();
            MacdPlusChangePoints = used.Select(a=>a.Value).ToImmutableArray();
            MacdPlusArr = arr.ToImmutableArray();
            DateArry= arr.Select(a => a.DateTime).ToArray();
        }
        public MacdPlusResult[] GetMacdPlus(int rowIndex, int count)
        {
            MacdPlusResult[] resultArr = new MacdPlusResult[count];

            if (rowIndex>= MacdPlusArr.Length)
            {
                return resultArr;
            }
            // 使用 BinarySearch 找到第一个终结点。
            int headIdx = Array.BinarySearch(Idxes, rowIndex); 
            //[1,2,6,8,9]
            // 0,1,2,3,4
            //找6返回2
            //找7返回-2,取返2+1=3
            //找10返回索引5，取反-4
            // 如果我自己就是终结点。
            headIdx=headIdx < 0? ~headIdx : headIdx;
 
            
            resultArr[0]= MacdPlusArr[rowIndex];
            for (int i = 1; i < count; i++)
            {
                if (headIdx+i<= MacdPlusChangePoints.Length)
                {
                    //考虑特殊情况，当我自己是终结点的时候，下一个终结点的前一个点不就是我自己么
                    resultArr[i] = MacdPlusChangePoints[headIdx + i-1];
                }
            }
            return resultArr;
        }

        public MacdPlusResult[] GetMacdPlus(DateTime date, int count)
        {
            // 使用 BinarySearch 找到第一个大于 x 的元素位置
            int rowIdx = Array.BinarySearch(DateArry, date);
            return GetMacdPlus(rowIdx, count);
        }
    }
    public class MACDInfo
    {
        public List<MacdResult> macds;
        public int fastPeriods = 12;
        public int slowPeriods = 26;
        public int signalPeriods = 9;

        public MACDInfo(List<FundHistory> his,int fastPeriods=12,int slowPeriods=26,int signalPeriods=9)
        {
            var temp=his.Select(a=>a.ToTuple()).ToList();
            this.fastPeriods = fastPeriods; 
            this.slowPeriods = slowPeriods;
            this.signalPeriods = signalPeriods;
            macds = temp.GetMacdResult(fastPeriods,slowPeriods,signalPeriods).ToList();
        }

        public static double GetMacd(double ema9,double ema12,double ema26)
        {
            return ema9 * 224 / 51f - ema12 * 16 / 3f + ema26 * 16 / 17f;
        }
        public static double GetMacd(List<FundHistory> his, DateTime today)
        {
            var oldFund = his.FindAll(a => a.date <= today).OrderBy(a => a.date).ToArray();
            List<double> dwjzs = oldFund.Select(a => a.dwjz).ToList();
            double ema9 = Indicator.CalculateEMA(dwjzs, 9);
            double ema12 = Indicator.CalculateEMA(dwjzs, 12);
            double ema26= Indicator.CalculateEMA(dwjzs, 26);
            return GetMacd(ema9,ema12, ema26);
        }
    }
}
